﻿//  Copyright (c) 2011 Ray Liang (http://www.dotnetage.com)
//  Licensed MIT: http://www.opensource.org/licenses/mit-license.php

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DNA.Mvc.Publishing.Data.Entity
{
    public class ArticleRepository : GenericPubEntityRepository<Article>, IArticleRepository
    {
        public ArticleRepository() : base() { }

        public ArticleRepository(PubDB db) : base(db) { }

        public int CountForCategory(int categoryID)
        {
            return DbSet.Count(a => a.CategoryID == categoryID);
        }

        public IQueryable<Article> Includes(int[] articlesIDs)
        {
            return DbSet.Where(a => articlesIDs.Contains(a.ID));
        }

        public void Move(int articleID, int pos, int categoryID)
        {
            var article = Find(articleID);

            if (article == null)
                throw new Exception(string.Format("Article {0} not found", articleID));
            var seqCollection = DbSet.Where(p => p.CategoryID == categoryID)
                                                .OrderBy(p => p.Pos)
                                                .Select(p => p.Pos);

            if (seqCollection.Count() == 0)
            {
                article.Pos = 0;
                article.CategoryID = categoryID;
                if (IsOwnContext)
                    Context.SaveChanges();
                return;
            }

            int upperBound = seqCollection.Max();
            int lowerBound = seqCollection.Min();

            int _from = article.Pos;
            int _to = pos;

            if (_to > upperBound)
                _to = upperBound;
            else
            {
                if (_to < lowerBound)
                    _to = lowerBound;
            }

            //1.Move up
            if (_from > _to)
            {
                DbSet.Where(p => p.CategoryID == categoryID && p.Pos >= _to && p.Pos < _from)
                    .OrderBy(p => pos)
                    .AsParallel()
                    .ForAll(p => p.Pos++);

            }

            //2.Move down
            if (_from < _to)
            {
                DbSet.Where(p => p.CategoryID == categoryID && p.Pos > _from && p.Pos <= _to)
                   .OrderBy(p => pos)
                   .AsParallel()
                   .ForAll(p => p.Pos--);
            }

            article.Pos = _to;
            article.Category = Context.Set<Category>().Find(categoryID);

            if (IsOwnContext) Context.SaveChanges();
        }

        public Article Vote(int articleID, int value, string ip, string userName)
        {
            var article = DbSet.Find(articleID);
            if (article.Votes.FirstOrDefault(v => v.UserName.Equals(userName) || v.IP.Equals(ip)) == null) 
            {
                var vote = new Vote()
                {
                    Article = article,
                    IP = ip,
                    Rating = value,
                    UserName = userName
                };
                Context.Set<Vote>().Add(vote);
                
                if (IsOwnContext)
                    Context.SaveChanges();
            }
            return article;
        }

        public Article Find(Uri uri)
        {
            string path = uri.LocalPath.ToLower().Replace("/publishing", "");
            return DbSet.FirstOrDefault(a => a.PermaLink.Equals(path, StringComparison.OrdinalIgnoreCase));
        }

        public IQueryable<Article> GetRecentPosts(int maxResults)
        {
            return dbSet.OrderByDescending(a => a.Posted)
                .Where(a => a.IsAppoved && a.IsPublished)
                .Take(maxResults);
        }

        public IQueryable<Article> GetChildren(int parentID)
        {
            return DbSet.Where(a => a.ParentID == parentID);
        }

        public bool HasChildren(int id)
        {
            return DbSet.Count(a => a.ParentID == id) > 0;
        }

        public Article Read(int id, string userName, string ip, string userAgent)
        {
            var article = Find(id);
            var start = DateTime.Now.AddHours(-1);
            var end = DateTime.Now;
            var tracks = article.Tracks.Where(t => (t.HasRead > start && t.HasRead < end));

            if (tracks.Count() > 0)
            {
                if (tracks.FirstOrDefault(t => t.IPAddress.Equals(ip, StringComparison.OrdinalIgnoreCase)) != null)
                    return article;
            }

            var track = new Track()
            {
                Article = article,
                HasRead = DateTime.Now,
                IPAddress = ip,
                UserAgent = userAgent,
                UserName = userName
            };

            Context.Set<Track>().Add(track);
            
            if (IsOwnContext)
                Context.SaveChanges();

            return article;
        }


        public int GetPosition(int categoryID)
        {
            if (DbSet.Count(a => a.CategoryID == categoryID) > 0)
            {
                return DbSet.Where(a => a.CategoryID == categoryID).Select(a => a.Pos).Max() + 1;
            }
            else
                return 0;
        }
    }
}
